﻿/*
   Open Intel 
   Copyright © 2011 – ISC. All Rights Reserved.
*/

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Printing;
using Caliburn.Micro;
using ISC.MapDotNetServer.Common;
using ISC.MapDotNetServer.Common.Maps;
using ISC.RIM.Silverlight.MapViewElement;
using ISC.RIM.Silverlight.ShapeEditing;
using OI.Async;
using OI.Controls;
using OI.DataModel;
using OI.Events;
using OI.Framework;
using OI.Views;
using Action = System.Action;
using Map = ISC.RIM.Silverlight.Map;

namespace OI.ViewModels
{
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class MashupToolboxViewModel : Screen, 
        IHandle<ChooseToolboxItem>, 
        IHandle<DisplayRIMMap>, 
        IHandle<SelectMap>, 
        IHandle<DragElementCompleted>, 
        IHandle<MouseLeftClickOnMap>,
        IHandle<SaveState>,
        IHandle<RestoreState>
    {
        #region Toolbox items
        public Toolbox QueryToolbox { get; private set; }
        public BindableCollection<ToolboxItem> QueryToolboxItems = new BindableCollection<ToolboxItem>()
        {
            new ToolboxItem() {Group = "QueryToolboxItems", ImageSize = 26, ImageSource=new Uri("/Images/Tools/QueryDrillDown.png", UriKind.Relative), 
                IsEnabled = true, IsSelected = true, Key = "QueryDrillDown", Tooltip = "query by drill-down"},
            new ToolboxItem() {Group = "QueryToolboxItems", ImageSize = 26, ImageSource=new Uri("/Images/Tools/QueryRectangle.png", UriKind.Relative), 
                IsEnabled = true, IsSelected = false, Key = "QueryRectangle", Tooltip = "query by rectangle"},
            new ToolboxItem() {Group = "QueryToolboxItems", ImageSize = 26, ImageSource=new Uri("/Images/Tools/QueryPolygon.png", UriKind.Relative), 
                IsEnabled = true, IsSelected = false, Key = "QueryPolygon", Tooltip = "query by polygon"},
            new ToolboxItem() {Group = "QueryToolboxItems", ImageSize = 26, ImageSource=new Uri("/Images/Tools/QueryPolyline.png", UriKind.Relative), 
                IsEnabled = true, IsSelected = false, Key = "QueryPolyline", Tooltip = "query by polyline"},
        };

        public Toolbox DrawToolbox { get; private set; }
        public BindableCollection<ToolboxItem> DrawToolboxItems = new BindableCollection<ToolboxItem>()
        {
            new ToolboxItem() {Group = "DrawToolboxItems", ImageSize = 26, ImageSource=new Uri("/Images/Tools/DrawRectangle.png", UriKind.Relative), 
                IsEnabled = true, IsSelected = false, Key = "DrawRectangle", Tooltip = "draw rectangle"},
            new ToolboxItem() {Group = "DrawToolboxItems", ImageSize = 26, ImageSource=new Uri("/Images/Tools/DrawPolygon.png", UriKind.Relative), 
                IsEnabled = true, IsSelected = false, Key = "DrawPolygon", Tooltip = "draw polygon"},
            new ToolboxItem() {Group = "DrawToolboxItems", ImageSize = 26, ImageSource=new Uri("/Images/Tools/DrawPolyline.png", UriKind.Relative), 
                IsEnabled = true, IsSelected = false, Key = "DrawPolyline", Tooltip = "draw polyline"},
             new ToolboxItem() {Group = "DrawToolboxItems", ImageSize = 26, ImageSource=new Uri("/Images/Tools/DrawPoint.png", UriKind.Relative), 
                IsEnabled = true, IsSelected = false, Key = "DrawPoint", Tooltip = "draw point"},
            new ToolboxItem() {Group = "DrawToolboxItems", ImageSize = 26, ImageSource=new Uri("/Images/Tools/DrawText.png", UriKind.Relative), 
                IsEnabled = true, IsSelected = false, Key = "DrawText", Tooltip = "draw text"},
            new ToolboxItem() {Group = "DrawToolboxItems", ImageSize = 26, ImageSource=new Uri("/Images/Tools/Erase.png", UriKind.Relative), 
                IsEnabled = true, IsSelected = false, Key = "Erase", Tooltip = "erase last drawn shape"}
        };

        public Toolbox ShareToolbox { get; private set; }
        public BindableCollection<ToolboxItem> ShareToolboxItems = new BindableCollection<ToolboxItem>
                                                                       {
            new ToolboxItem() {Group = "ShareToolboxItems", ImageSize = 26, ImageSource=new Uri("/Images/Tools/Save.png", UriKind.Relative), 
                IsEnabled = true, IsSelected = false, Key = "Save", Tooltip = "share my map"},
            new ToolboxItem() {Group = "ShareToolboxItems", ImageSize = 26, ImageSource=new Uri("/Images/Tools/Print.png", UriKind.Relative), 
                IsEnabled = true, IsSelected = false, Key = "Print", Tooltip = "print my map"},
            new ToolboxItem() {Group = "ShareToolboxItems", ImageSize = 26, ImageSource=new Uri("/Images/Tools/PrintData.png", UriKind.Relative), 
                IsEnabled = true, IsSelected = false, Key = "PrintData", Tooltip = "print my data"},
            new ToolboxItem() {Group = "ShareToolboxItems", ImageSize = 26, ImageSource=new Uri("/Images/Tools/AccessFeeds.png", UriKind.Relative), 
                IsEnabled = true, IsSelected = false, Key = "AccessFeeds", Tooltip = "launch access feeds dialog"}
        };
        #endregion

        #region DATA
        readonly private IEventAggregator Events;
        readonly private IWindowManager WindowManager;
        private Map RIMMap;
        private ISC.MapDotNetServer.Common.Maps.Map Map;
        private MeasurementManager CurrentMeasurementManager;

        // shared spatial filter
        public Shape SpatialFilter { get; set; }

        // drawn shape stack
        public Stack<Guid> DrawnShapeIDs =
            new Stack<Guid>();

        // place one-shots tracking
        private bool PlacePointOneShot;
        private bool PlaceTextOneShot;

        public bool ToolsReady
        {
            get
            {
                return _ToolsReady;
            }
            set
            {
                _ToolsReady = value;
                NotifyOfPropertyChange(() => ToolsReady);
            }
        }
        private bool _ToolsReady;

        public string QueryHint
        {
            get { return _QueryHint; }
            set 
            { 
                _QueryHint = value;
                NotifyOfPropertyChange(() => QueryHint);
            }
        }
        private string _QueryHint;

        public bool QueryMode
        {
            get
            {
                return CanDoQuery;
            }
        }

        public enum QueryDrawEditModes { Draw, Edit }
        public QueryDrawEditModes QueryDrawEditMode
        {
            get
            {
                return _QueryDrawEditMode;
            }
            set
            {
                _QueryDrawEditMode = value;

                var shapeEditor = RIMMap.MapModel.ShapeEditManagerInProgress;
                if (shapeEditor != null)
                {
                    // finish incomplete parts before switching modes
                    shapeEditor.FinishNewIncompletePart(true);

                    // setup editor based on mode
                    if (_QueryDrawEditMode == QueryDrawEditModes.Draw)
                    {
                        shapeEditor.ClearPartSelection();
                        shapeEditor.PartEditMode = ShapeEditManager.PartEditModes.NewPart;
                    }
                    else
                    {
                        shapeEditor.PartEditMode = ShapeEditManager.PartEditModes.SingleSelected;
                    }
                }

                NotifyOfPropertyChange(() => QueryDrawEditModeText);
            }
        }
        private QueryDrawEditModes _QueryDrawEditMode;

        public string QueryDrawEditModeText
        {
            get
            {
                return QueryDrawEditMode.ToString().ToLower();
            }
        }

        public string DrawingHint
        {
            get { return _DrawingHint; }
            set
            {
                _DrawingHint = value;
                NotifyOfPropertyChange(() => DrawingHint);
            }
        }
        private string _DrawingHint;

        public string Measurement
        {
            get { return _Measurement; }
            set
            {
                _Measurement = value;
                NotifyOfPropertyChange(() => Measurement);
            }
        }
        private string _Measurement;


        public string ToolCommand
        {
            get
            {
                return _ToolCommand;
            }
            set
            {
                _ToolCommand = value;
                NotifyOfPropertyChange(() => CanDoQuery);
                NotifyOfPropertyChange(() => QueryMode);
                NotifyOfPropertyChange(() => CanCancelQuery);
                NotifyOfPropertyChange(() => CanClearQueryShape);
                NotifyOfPropertyChange(() => CanChangeQueryDrawEditMode);
                NotifyOfPropertyChange(() => CanDoDraw);
                NotifyOfPropertyChange(() => CanCancelDraw);
                NotifyOfPropertyChange(() => CanUnits);
            }
        }
        private string _ToolCommand = string.Empty;


        // can properties for link enabling through conventions
        public bool CanDoQuery
        {
            get
            {
                return ToolCommand == "QueryPolygon" ||
                       ToolCommand == "QueryPolyline" &&
                       ToolsReady;
            }
        }
        public bool CanCancelQuery
        {
            get
            {
                return ToolCommand == "QueryPolygon" ||
                       ToolCommand == "QueryPolyline" && 
                       ToolsReady;
            }
        }
        public bool CanClearQueryShape
        {
            get
            {
                return ToolCommand == "QueryPolygon" ||
                       ToolCommand == "QueryPolyline" &&
                       ToolsReady; 
            }
        }
        public bool CanChangeQueryDrawEditMode
        {
            get
            {
                return ToolCommand == "QueryPolygon" ||
                       ToolCommand == "QueryPolyline" &&
                       ToolsReady;
            }
        }
        public bool CanDoDraw
        {
            get
            {
                return ToolCommand == "DrawPolygon" ||
                       ToolCommand == "DrawPolyline" &&
                       ToolsReady;
            }
        }
        public bool CanCancelDraw
        {
            get
            {
                return ToolCommand.Contains("Draw") && 
                       ToolsReady;
            }
        }
        public bool CanUnits
        {
            get
            {
                return ToolCommand == "DrawPolygon" ||
                       ToolCommand == "DrawPolyline" &&
                       ToolsReady;
            }
        }
        
        public SolidColorBrush ColorSwatch
        {
            get
            {
                return MapShapes.OutlineBrush;
            }
        }

        #endregion


        [ImportingConstructor]
        public MashupToolboxViewModel(IEventAggregator events, IWindowManager windowManager)
        {
            events.Subscribe(this);
            Events = events;
            WindowManager = windowManager;
        }


        protected override void OnViewLoaded(object view)
        {
            base.OnViewLoaded(view);
            var v = (MashupToolboxView)view;

            // load itemsources for toolboxes
            QueryToolbox = v.QueryToolbox;
            QueryToolbox.Items = QueryToolboxItems;

            DrawToolbox = v.DrawToolbox;
            DrawToolbox.Items = DrawToolboxItems;

            ShareToolbox = v.ShareToolbox;
            ShareToolbox.Items = ShareToolboxItems;

        }


        // called as view is deactivated - clean up and close
        protected override void OnDeactivate(bool close)
        {
            Cancel();
            TurnAllToolsOff();
            ToolsReady = false;
            base.OnDeactivate(close);
        }


        // handle when a RIM Map is ready
        // grab a reference here since most toolbox items must manipulate the map
        public void Handle(DisplayRIMMap message)
        {
            RIMMap = message.Map;
            ToolsReady = true;
        }


        // when a Common.Maps.Map is selected for a given RIMMap
        public void Handle(SelectMap message)
        {
            Map = message.Map;
        }


        // handle when a toolbox item is selected
        // we will route all toolbox selections to the appropriate component
        public void Handle(ChooseToolboxItem message)
        {
            // reset for new query
            Cancel();

            // delect other groups
            if (!string.IsNullOrEmpty(message.Group) &&
                QueryToolbox != null && 
                DrawToolbox != null &&
                ShareToolbox != null)
            {
                switch (message.Group)
                {
                    case "QueryToolboxItems":
                        DrawToolbox.DeselectAll();
                        ShareToolbox.DeselectAll();
                        break;
                    case "DrawToolboxItems":
                        QueryToolbox.DeselectAll();
                        ShareToolbox.DeselectAll();
                        break;
                    case "ShareToolboxItems":
                        QueryToolbox.DeselectAll();
                        DrawToolbox.DeselectAll();
                        break;
                }
            }

            // start new operation
            ToolCommand = message.Key;
            if (RIMMap != null)
            {
                switch (ToolCommand)
                {
                    // querying --------------
                    case "QueryDrillDown":
                        QueryHint = "Click on the map to drill-down and identify features";
                        Events.Publish(new EnableDrillDown
                        {
                            Enable = true
                        });
                        break;
                    case "QueryRectangle":
                        QueryHint = "click-drag a rectangle around the features to query";
                        RIMMap.MapController.BeginDragElement(DragElement.AnchorOrigins.Corner, false, false);
                        break;
                    case "QueryPolygon":
                        QueryHint = "click verticies to draw a polygon around the features to query";

                        if (SpatialFilter is Polygon)
                        {
                            // existing
                            RIMMap.MapController.BeginPolygonEdit((Polygon)SpatialFilter);
                            QueryDrawEditMode = QueryDrawEditModes.Edit;
                        }
                        else
                        {
                            // new
                            RIMMap.MapController.BeginPolygonEdit();
                            QueryDrawEditMode = QueryDrawEditModes.Draw;
                        }
                        
                        break;
                    case "QueryPolyline":
                        QueryHint = "click verticies to draw a polyline intersecting features to query";

                        if (SpatialFilter is Polyline)
                        {
                            // existing
                            RIMMap.MapController.BeginPolylineEdit((Polyline)SpatialFilter);
                            QueryDrawEditMode = QueryDrawEditModes.Edit;
                        }
                        else
                        {
                            // new
                            RIMMap.MapController.BeginPolylineEdit();
                            QueryDrawEditMode = QueryDrawEditModes.Draw;
                        }
                        break;
                   
                    // drawing --------------
                    case "DrawRectangle":
                        DrawingHint = "click-drag a rectangle to draw it on the map";
                        RIMMap.MapController.BeginDragElement(DragElement.AnchorOrigins.Corner, false, false);
                        break;
                    case "DrawPolygon":
                        DrawingHint = "click verticies to draw a polygon on the map";
                        var pge = RIMMap.MapController.BeginPolygonEdit();
                        CurrentMeasurementManager = new MeasurementManager(
                            RIMMap.TileProjection,
                            (s) => Measurement = s,
                            pge);
                        break;
                    case "DrawPolyline":
                        DrawingHint = "click verticies to draw a polyline on the map";
                        var ple = RIMMap.MapController.BeginPolylineEdit();
                        CurrentMeasurementManager = new MeasurementManager(
                            RIMMap.TileProjection,
                            (s) => Measurement = s,
                            ple);
                        break;
                    case "DrawPoint":
                        DrawingHint = "click map to place a point symbol";
                        PlacePointOneShot = true;
                        break;
                    case "DrawText":
                        DrawingHint = "click map to place text - hit return when done";
                        PlaceTextOneShot = true;
                        break;
                    case "Erase":
                        DrawingHint = "erasing...";
                        if (DrawnShapeIDs.Count > 0)
                        {
                            var id = DrawnShapeIDs.Pop();
                            Events.Publish(new ClearShape
                            {
                                ID = id
                            });
                        }
                        Cancel();
                        TurnAllToolsOff();
                        break;

                    // sharing --------------
                    case "Save":
                        SaveState();
                        Cancel();
                        TurnAllToolsOff();
                        break;
                    case "Print":
                        Print();
                        Cancel();
                        TurnAllToolsOff();
                        break;
                    case "PrintData":
                        PrintData();
                        Cancel();
                        TurnAllToolsOff();
                        break;
                    case "AccessFeeds":
                        AccessFeeds();
                        Cancel();
                        TurnAllToolsOff();
                        break;
                    default:
                        QueryHint = "";
                        break;
                }
            }
        }


        private void TurnAllToolsOff()
        {
            QueryToolbox.DeselectAll();
            DrawToolbox.DeselectAll();
            ShareToolbox.DeselectAll();
        }


        // subscribe to drag-element operation completed
        // we may want to query or draw the envelope dragged
        public void Handle(DragElementCompleted message)
        {
            if (ToolCommand == "QueryRectangle")
            {
                SpatialFilter = message.Rect.ToPolygon();
                DoQuery();
            }
            else if (ToolCommand == "DrawRectangle")
            {
                DrawShape(message.Rect.ToPolygon());
                Cancel();
            }
            TurnAllToolsOff();
        }


        // subscribe to map mouse left clicks
        // we may want to insert a text item
        public void Handle(MouseLeftClickOnMap message)
        {
            if (PlacePointOneShot || PlaceTextOneShot)
            {
                ShowShape.ShowShapeTypes sType = ShowShape.ShowShapeTypes.None;
                if (PlacePointOneShot)
                {
                    sType = ShowShape.ShowShapeTypes.DrawnPoint;
                }
                else if (PlaceTextOneShot)
                {
                    sType = ShowShape.ShowShapeTypes.DrawnText;
                }

                // start new point symbol
                var id = Guid.NewGuid();
                DrawnShapeIDs.Push(id);
                Events.Publish(new ShowShape
                {
                    ID = id,
                    LocationX = message.Location.MapPoint.X,
                    LocationY = message.Location.MapPoint.Y,
                    ShowShapeType = sType
                });

                PlaceTextOneShot = false;
                PlacePointOneShot = false;
                Cancel();
                TurnAllToolsOff();
            }
        }


        // reset toolbox operation
        private void Cancel()
        {
            string curCmd = ToolCommand;
            ToolCommand = "";

            if (RIMMap != null)
            {
                // always turn off
                Events.Publish(new EnableDrillDown
                {
                    Enable = false
                });

                if (curCmd == "QueryRectangle")
                {
                    RIMMap.MapController.EndDragElement();
                }
                else if (curCmd == "QueryPolygon")
                {
                    RIMMap.MapController.EndPolygonEdit(true);
                }
                else if (curCmd == "QueryPolyline")
                {
                    RIMMap.MapController.EndPolylineEdit(true);
                }
                else if (curCmd == "DrawRectangle")
                {
                    RIMMap.MapController.EndDragElement();
                }
                else if (curCmd == "DrawPolygon")
                {
                    RIMMap.MapController.EndPolygonEdit(true);
                }
                else if (curCmd == "DrawPolyline")
                {
                    RIMMap.MapController.EndPolylineEdit(true);
                }
            }

            if (CurrentMeasurementManager != null)
            {
                CurrentMeasurementManager.Disconnect();
            }

            PlaceTextOneShot = false;
            QueryHint = "";
            DrawingHint = "";
            Measurement = "";
        }


        // toggle between drawing and edit mode in polygon and polyline query shape mode
        public void ChangeQueryDrawEditMode()
        {
            QueryDrawEditMode = QueryDrawEditMode == QueryDrawEditModes.Draw ? 
                QueryDrawEditModes.Edit : 
                QueryDrawEditModes.Draw;
        }


        // action handler to execute spatial query
        public void DoQuery()
        {
            // if we were entering a PathElement
            if (Map != null &&
                RIMMap != null &&
                RIMMap.MapModel != null &&
                RIMMap.MapModel.ShapeEditManagerInProgress != null)
            {
                var shapeEditor = RIMMap.MapModel.ShapeEditManagerInProgress;

                // finish any incomplete shapes which can be auto-completed
                shapeEditor.FinishNewIncompletePart(true);

                if (shapeEditor.IsComplete)
                {
                    if (shapeEditor is PolygonEditor)
                    {
                        var pe = (PolygonEditor)shapeEditor;
                        SpatialFilter = pe.GetPolygonSnapshot();
                    }
                    else if (shapeEditor is PolylineEditor)
                    {
                        var pe = (PolylineEditor)shapeEditor;
                        SpatialFilter = pe.GetPolylineSnapshot();
                    }

                    // executing a query should put shape editor in edit mode
                    QueryDrawEditMode = QueryDrawEditModes.Edit;
                }
            }

            // if we have a spatial filter, sync-async the query
            if (SpatialFilter != null)
            {
                 Coroutine.BeginExecute(QueryIterator());
            }

        }


        // action handler for cancel query
        public void CancelQuery()
        {
            Cancel();
            TurnAllToolsOff();
            QueryDrawEditMode = QueryDrawEditModes.Draw;
        }

        public void ClearQueryShape()
        {
            var shapeEditor = RIMMap.MapModel.ShapeEditManagerInProgress;
            shapeEditor.DeleteAllParts();
            SpatialFilter = null;
        }


        // coroutine for async query
        public IEnumerator<IResult> QueryIterator()
        {
            Events.Publish(new ShowBusy { Caption = "performing spatial query..." });

            // serialize spatial filter
            string filterStr = SpatialFilter.Serialize();

            // loop multiple layers to query (drill-down)
            if (!string.IsNullOrEmpty(filterStr))
            {
                // note: currently, all visible layers are queried
                var layers = Map.Layers.Where((layer)=> layer.Visible).ToList();

                if (layers.Count > 0)
                {
                    var dsq = new MDNSpatialQuery(layers, filterStr);
                    yield return dsq;

                    // broadcast the arrival of query results
                    Events.Publish(new QueriedResultSets
                    {
                        Sources = layers,
                        Tables = dsq.Results,
                        ShowInDataArea = true
                    });
                }
            }

            Events.Publish(new HideBusy());
        }


        // finish drawing action handler
        public void DoDraw()
        {
            if (RIMMap != null &&
                RIMMap.MapModel != null &&
                RIMMap.MapModel.ShapeEditManagerInProgress != null)
            {
                var shapeEditor = RIMMap.MapModel.ShapeEditManagerInProgress;

                // finish any incomplete shapes which can be auto-completed
                if (!shapeEditor.IsComplete && shapeEditor.CanComplete)
                {
                    shapeEditor.FinishNewIncompletePart();
                }

                if (shapeEditor.IsComplete)
                {
                    if (shapeEditor is PolygonEditor)
                    {
                        var pe = (PolygonEditor)shapeEditor;
                        DrawShape(pe.GetPolygonSnapshot());
                    }
                    else if (shapeEditor is PolylineEditor)
                    {
                        var pe = (PolylineEditor)shapeEditor;
                        DrawShape(pe.GetPolylineSnapshot());
                    }
                }
            }
            Cancel();
            TurnAllToolsOff();
        }


        public void CancelDraw()
        {
            Cancel();
            TurnAllToolsOff();
        }


        // draws a shape and registers id in stack
        private void DrawShape(Shape shape)
        {
            var id = Guid.NewGuid();
            DrawnShapeIDs.Push(id);

            Events.Publish(new ShowShape
            {
                ID = id,
                ShowShapeType = ShowShape.ShowShapeTypes.DrawnShape,
                ShapeSerial = shape.Serialize()
            });
        }


        // action handler for when units link clicked
        public void Units()
        {
            if (CurrentMeasurementManager != null)
            {
                CurrentMeasurementManager.NextUnits();
            }
        }


        // action handler for when colors link clicked
        public void Colors()
        {
            MapShapes.NextDrawingColor();
            NotifyOfPropertyChange(() => ColorSwatch);
        }


        #region PRINT MAP
        private bool IsPrinting;
        private void Print()
        {
            if (!IsPrinting && RIMMap != null)
            {
                var doc = new PrintDocument();
                doc.BeginPrint += BeginPrint;
                doc.PrintPage += PrintPage;
                doc.EndPrint += EndPrint;
                doc.Print("Printing: " + Configuration.Jurisdiction.TitleBlock);
            }
        }


        void BeginPrint(object sender, BeginPrintEventArgs e)
        {
            IsPrinting = true;
            Events.Publish(new ShowBusy { Caption = "printing map..." });
        }

        void PrintPage(object sender, PrintPageEventArgs e)
        {
            var lvm = IoC.Get<MashupLegendViewModel>("MashupLegendViewModel");
            var legendItems = new BindableCollection<LegendItem>(lvm.Items);

            var vm = new PrintViewModel
            {
                TitleBlock = Configuration.Jurisdiction.TitleBlock,
                MapImage = new WriteableBitmap(RIMMap, null),
                Items = legendItems
            };

            var view = new PrintView();
            ViewModelBinder.Bind(vm, view, null);

            var printable = new Viewbox
            {
                Width = e.PrintableArea.Width,
                Height = e.PrintableArea.Height,
                Child = view
            };
            printable.UpdateLayout();

            e.PageVisual = printable;
            e.HasMorePages = false;
        }

        void EndPrint(object sender, EndPrintEventArgs e)
        {
            IsPrinting = false;
            Events.Publish(new HideBusy());
        }
        #endregion

        #region PRINT DATA
        private int _DatasetIdx;
        private int _RowIdx;
        private void PrintData()
        {
            if (!IsPrinting && RIMMap != null)
            {
                _DatasetIdx = 0;
                _RowIdx = 0;

                var doc = new PrintDocument();
                doc.BeginPrint += BeginDataPrint;
                doc.PrintPage += PrintDataPage;
                doc.EndPrint += EndDataPrint;
                doc.Print("Printing: " + Configuration.Jurisdiction.TitleBlock);
            }
        }

        void BeginDataPrint(object sender, BeginPrintEventArgs e)
        {
            IsPrinting = true;
            Events.Publish(new ShowBusy { Caption = "printing data..." });
        }

        void PrintDataPage(object sender, PrintPageEventArgs e)
        {
            var dvm = IoC.Get<MashupDataViewModel>();
            var lastDisplayedResults = dvm.LastDisplayedResults;

            if (_DatasetIdx < lastDisplayedResults.Tables.Count())
            {
                var parent = new StackPanel
                {
                    Margin = new Thickness(32)
                };

                var dg = new DataGrid
                {
                    Style = Application.Current.Resources["OIDataGrids"] as Style
                };

                parent.Children.Add(dg);

                var table = lastDisplayedResults.Tables[_DatasetIdx];
                var layer = lastDisplayedResults.Sources != null ? lastDisplayedResults.Sources[_DatasetIdx] : null;
                var headings = (layer != null &&
                           layer.MetaData != null &&
                           layer.HasDrilldownHeaders()) ?
                           new List<string>(layer.GetDrilldownHeaders().Split(',')) :
                           table.Fields;

                // get hyperlink columns
                var hyperlinkCols = layer.GetHyperlinkColumns();

                // build a StringTable from the results
                var st = new StringTable(headings, hyperlinkCols, true);

                // rows per page calc
                int rowsPerPage = Convert.ToInt32(e.PrintableArea.Height - 64) / 30;

                // add rows
                int rowIdx, rowCount = table.QueryResultRows.Count;
                for (rowIdx = _RowIdx;
                     rowIdx < _RowIdx + rowsPerPage && rowIdx < rowCount;
                     rowIdx++)
                {
                    var row = table.QueryResultRows[rowIdx];
                    st.AddRow(row.Values, row.Shape);
                }

                // if next dataset
                _RowIdx = rowIdx;
                if (rowIdx >= rowCount)
                {
                    // skip over empty datasets
                    while (true)
                    {
                        _DatasetIdx++;
                        _RowIdx = 0;

                        if (_DatasetIdx < lastDisplayedResults.Tables.Count())
                        {
                            var t = lastDisplayedResults.Tables[_DatasetIdx];
                            if (t.QueryResultRows.Count > 0)
                            {
                                break;
                            }
                        }
                        else
                        {
                            break;
                        }
                    }
                }
                
                // bind
                st.BindStringTableToDataGrid(dg);

                var printable = new Viewbox
                {
                    Width = e.PrintableArea.Width,
                    Height = e.PrintableArea.Height,
                    Child = parent 
                };
                printable.UpdateLayout();

                e.PageVisual = printable;
            }

            e.HasMorePages = _DatasetIdx < lastDisplayedResults.Tables.Count();
        }

        void EndDataPrint(object sender, EndPrintEventArgs e)
        {
            IsPrinting = false;
            Events.Publish(new HideBusy());
        }
        #endregion

        #region SHARING
        private void SaveState()
        {
            var id = Guid.NewGuid();

            // build share uri and display notice (copy to clipboard) after we save
            Action postSave = () =>
            {
                string url = Configuration.GetBaseURI() + 
                    Configuration.StartPage + 
                    "?" + 
                    Configuration.QueryStringJurisdictionID +
                    "=" +
                    Configuration.Jurisdiction.JurisdictionID +
                    "&" +
                    Configuration.QueryStringShareID + 
                    "=" + 
                    id;

                var vm = new ShareNoticeViewModel(url);
                WindowManager.ShowDialog(vm);
            };

            // request SharingManager to begin save
            Events.Publish(new SaveStateRequest
            {
                ID = id,
                PostSave = postSave
            });
        }


        private void AccessFeeds()
        {
            if (Map != null && RIMMap != null)
            {
                var vm = new FeedLinksViewModel(Map.Layers, RIMMap.MapView.GetMapExtents());
                WindowManager.ShowDialog(vm);
            }
        }

        public void Handle(RestoreState message)
        {
            string spatialFilterSerial = message.Read(GetType().ToString());
            if (!string.IsNullOrEmpty(spatialFilterSerial))
            {
                SpatialFilter = Shape.DeserializeShape(spatialFilterSerial);
            }
        }


        public void Handle(SaveState message)
        {
            // write to state
            message.Write(GetType().ToString(), SpatialFilter != null ? SpatialFilter.Serialize() : null);
        }

        #endregion
    }
}
